package com.ixion.mongo.utils;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;

import org.bson.types.ObjectId;
import org.nutz.castor.Castors;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.util.NutMap;

import com.ixion.mongo.annotation.Collection;
import com.ixion.mongo.annotation.Sequence;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;

public class Mongos {
	// Mongo客户端连接
	protected static MongoClient client;
	// 当前线程
	protected static ThreadLocal<MongoClient> local = new ThreadLocal<MongoClient>();
	// db对象
	protected static DB db;
	protected static Properties prop = new Properties();;

	/**
	 * 客户端连接
	 */
	private static Connector connector;

	// 从配置文件加载信息并连接mongo服务器，获取DB对象
	static {
		// 如果当前线程已经加载过相关内容则不做任何处理没有加载过配置信息即加载配置进行初始化

		try {
			prop.load(Mongos.class.getClassLoader().getResourceAsStream("config/mongo.properties"));
			if (local == null || local.get() == null) {
				client = new MongoClient(prop.getProperty("server"), Integer.parseInt(prop.getProperty("port")));
				local.set(client);
			}
			connector = Connector.instants(prop.getProperty("server"), Integer.parseInt(prop.getProperty("port")));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取mongo连接
	 * 
	 * @return
	 */
	// FIXME Judge if the client is closed
	public static MongoClient getClient() {
		return connector;
	}

	public static boolean closeConnection() {
		MongoClient client = local.get();
		if (client != null) {
			try {
				client.close();
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}
		} else {
		}
		return true;
	}

	/**
	 * 获取配置的数据库DB对象
	 * 
	 * @return
	 */
	public static DB getDb() {
		return DBConnection.me().getDbConnection(connector, prop.getProperty("dbName"), prop.getProperty("user"), prop.getProperty("pwd"));
	}

	/**
	 * 获取指定名称的数据库DB对象
	 * 
	 * @param dbName
	 * @return
	 */
	public static DB getDb(String dbName) {
		return client.getDB(dbName);
	}

	/**
	 * POJO对象转换成Map
	 * 
	 * @param o
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static NutMap obj2map(Object o) {
		// 空
		if (null == o)
			return new NutMap();
		// XXX this is not used
		// // MoChain
		// if (o instanceof MoChain)
		// return ((MoChain) o).toMap();

		// NutMap
		if (o instanceof NutMap)
			return (NutMap) o;

		// 字符串
		if (o instanceof CharSequence)
			return Json.fromJson(NutMap.class, o.toString());

		// 更多判断 ...
		Mirror<?> mirror = Mirror.me(o.getClass());

		// 普通 Map
		if (mirror.isMap()) {
			return new NutMap((Map<String, Object>) o);
		}

		// POJO
		if (mirror.isPojo())
			return new NutMap(Lang.obj2map(o));

		// 其他的，调用 Castors 先变 Map 再说
		return Castors.me().castTo(o, NutMap.class);
	}

	/**
	 * POJO转换成Mongo的DBObject
	 * 
	 * @param o
	 * @return
	 */
	public static DBObject obj2dbo(Object o) {
		return map2dbo(obj2map(o), o);
	}

	/**
	 * Map对象转换成Mongo的DBObject
	 * 
	 * @param map
	 * @param o
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static DBObject map2dbo(Map<String, Object> map, Object o) {
		DBObject dbo = new BasicDBObject();
		if (null != map) {
			for (Map.Entry<String, ? extends Object> en : map.entrySet()) {
				String key = en.getKey();
				Object val = en.getValue();
				if (null == val)
					continue;
				// 如果是 id而且未赋值
				if ("id".equals(key) && val.equals(new Long(0)) && !(o instanceof Map)) {
					val = autoInc(getSeqName(o));
				}
				// 如果是枚举
				else if (val.getClass().isEnum())
					val = val.toString();

				// 如果是 Map，递归
				else if (val instanceof Map<?, ?>)
					val = map2dbo((Map<String, Object>) val, val);

				// 加入 DBObject
				if ("id".equals(key)) {
					key = "_id";
				}
				dbo.put(key, val);
			}
		}
		return dbo;
	}

	public static String getSeqName(Object o) {
		if (o instanceof Class<?>) {
			Sequence seq = ((Class<?>) o).getAnnotation(Sequence.class);
			if (seq == null) {
				Class<?> clazz = (Class<?>) o;
				return clazz.getSimpleName().toLowerCase() + "_id";
			}
			return seq.value();
		}
		Sequence seq = o.getClass().getAnnotation(Sequence.class);
		// 未注解的情况 返回 类名_id做为当前类的id序列名称
		if (seq == null) {
			Class<?> clazz = o.getClass();
			return clazz.getSimpleName().toLowerCase() + "_id";
		}
		// 返回注解值
		return seq.value();
	}

	public static NutMap dbo2map(DBObject dbo) {
		if (null == dbo)
			return null;
		NutMap map = new NutMap();
		for (String key : dbo.keySet()) {
			Object dbval = dbo.get(key);
			if (dbval instanceof ObjectId)
				map.put(key, dbval.toString());
			// fix user set id
			else if (key.equals("_id")) {
				map.put("id", dbval);
			} else
				map.put(key, dbval);
		}
		return map;
	}

	public static int autoInc(String seqName) {
		DBObject seq = new BasicDBObject().append("name", seqName);
		DBObject inc = new BasicDBObject("$inc", new BasicDBObject().append("id", 1));
		DBCollection incc = getDb().getCollection("inc_ids");
		if (incc == null) {
			incc = createCollection("inc_ids");
		}
		return (Integer) incc.findAndModify(seq, null, null, false, inc, true, true).get("id");
	}

	public static int getCur(String seqName) {
		DBObject seq = new BasicDBObject().append("name", seqName);
		DBCollection incc = getDb().getCollection("inc_ids");
		return (Integer) incc.findOne(seq).get("id");
	}

	public static DBCollection createCollection(String name) {
		return getDb().createCollection(name, new BasicDBObject());
	}

	public static DBCollection getCollection(Object object) {
		String name = "";
		Collection collection = null;
		Class<?> clazz = null;
		if (object instanceof Class<?>) {
			collection = ((Class<?>) object).getAnnotation(Collection.class);
			clazz = (Class<?>) object;
		} else {
			collection = object.getClass().getAnnotation(Collection.class);
			clazz = object.getClass();
		}
		if (collection == null) {
			name = clazz.getSimpleName().toLowerCase();
		} else {
			name = collection.value();
		}
		return getDb().getCollection(name);
	}
}
